/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */

package ctrl.sys;

import mod.dao.tables;
import java.io.*;
import java.net.*;
import java.sql.ResultSet;
import java.util.ArrayList;
import ctrl.jeu.Carte;
import ctrl.jeu.Mise;
import ctrl.jeu.Place;
import ctrl.jeu.Table;
import ctrl.jeu.Chaise;
import java.util.Collections;


public class serveurTCP{
    ArrayList<Table> Listetable; // liste des tables sur le serveur
    ArrayList<Client> ListeClient; // Liste des clients connectés
    ArrayList<Client> ListeObservateur; // Liste des observateurs

    public serveurTCP() {
        
        Listetable=new ArrayList<Table>();
        ListeClient=new ArrayList<Client>();
        ListeObservateur=new ArrayList<Client>();
        // lecture des tables en bd et création des instances
        tables tabledao=new tables();
        ResultSet r=tabledao.Liste();
        try {
            while (r.next())
            {
                Listetable.add(new Table(r.getInt("id"),r.getInt("blind"),r.getInt("min"),r.getInt("max")));
                //break;
            }
        }catch(Exception e)
        {

        }
        //Listetable.add(new Table(1,10,100,5000));
    }

    public static void main(String[] arg)
    {
        serveurTCP se=new serveurTCP();
        se.Launch();
    }

    public ArrayList<Client> getListeObservateur() {
        return ListeObservateur;
    }


    public ArrayList<Table> getListetable() {
        return Listetable;
    }

    public ArrayList<Client> getListeClient() {
        return ListeClient;
    }

    public Place initTour(Table desk)
    {
        //on vide la liste des participants aux tours
        desk.getParticipantTour().clear();
        desk.getMises().clear();
        Carte[] c=new Carte[3];
        desk.setFlop(c);
        desk.setTournant(null);
        desk.setRiviere(null);
        // on mets a jour les dealer les seed
        desk.setDealer(desk.getPlaces().get(0));
        desk.SetSeed();
        desk.ShowCartePartage();
        // on initialise la liste des participants au tour
        desk.initParticipant();
        //small blind

        Place small=desk.getPlaces((desk.getDealer().getId()+1)%desk.getChaises().size());
        //System.out.println("small= "+small);
        desk.getMises().add(new Mise(small,desk.getBlind()/2));
        small.setMontantPartie(small.getMontantPartie()-desk.getBlind()/2);
        //big blind
        Place big=desk.getPlaces((desk.getDealer().getId()+2)%desk.getChaises().size());
        desk.getMises().add(new Mise(big,desk.getBlind()));
        big.setMontantPartie(big.getMontantPartie()-desk.getBlind());
        // on initialise zone comme le joueur en cours
        return big;
    }
    public synchronized void MAJ(ObjectOutputStream cli,Demande ask)
    {
        int act=ask.getAction(); // recuperation de l'action demandée
        Table desk= Table.getTable(getListetable(),ask.getIdTable()); // on recupère la table tel que l'id est celui demandé
        int numCh=ask.getIdChaise(); // on recupère la place du client
        Place zone=null;
        Chaise chaise_coor=desk.getChaises(numCh);
        
        if(chaise_coor!=null) // si le client est déjà assis on recupère sa place
        if(chaise_coor.getLieu()!=null)
        {
            zone = chaise_coor.getLieu();
        }
        
        Reponse resp=new Reponse(); // on cree un nouvel objet reponse
        switch (act)
        {
            // si le client veut regarder
            case Const.ENTER:
            {
                System.out.println("demande d'entrée ");
                int idt=-1;
                if(desk.getObservateurs().size()>0)
                {
                    idt=Collections.max(desk.getObservateurs()).getIdChaise()-1;
                }
                /*ArrayList<Place> lst=desk.getObservateurs();
                if(lst.size()>0) idt=lst.get(lst.size()-1).getId()-1;*/
                Place p=new Place(ask.getMontant(),ask.getJ(),idt);
                desk.getObservateurs().add(p); // on ajoute aux observateur la place du client
                zone=desk.getObservateurs().get(desk.getObservateurs().indexOf(p));
                getListeObservateur().add(new Client(zone.getId(),cli));
                break;
            }
            // si le client a demandé a s'asseoir
            case Const.SIT :
            {
                System.out.println("demande de sit ");
                Place p=new Place(ask.getMontant(),ask.getJ(),ask.getIdChaise());
                desk.getPlaces().add(p); // on ajoute aux places la place du client
                //desk.getObservateurs().remove(p); // on supprime de la liste des observateurs
                zone=desk.getPlaces().get(desk.getPlaces().indexOf(p));
                getListeClient().add(new Client(zone.getId(),cli)); // met la socket client dans a liste.
                getListeObservateur().remove(new Client(zone.getId(),cli));
                // on met les places sur les chaises
                chaise_coor.setLieu(zone);
                //si la la table est remplie on crée le seed
                if(desk.isPlein()&&!desk.islaunch)
                {
                    zone=initTour(desk);
                    resp.setParcours(true);
                }
                resp.setTexte("<strong>"+ask.getJ()+"</strong> a rejoint la table");
                break;
            }
            case Const.DEBUT :
            {
                System.out.println("Recomencer");
                zone=initTour(desk);
                resp.setParcours(true);
                resp.setTexte("<hr />");
                break;
            }
            case Const.CALL :
            {
                //Mise mise_max=Collections.max(desk.getMises());
                Mise ma_mise=desk.getMises(zone);
                float mnt=ask.getMontant();
                zone.setMontantPartie(zone.getMontantPartie()-mnt); // met a jour le montant que possède le joueur
                ma_mise.setMontantTotal(ma_mise.getMontantTotal()+mnt); // met a jour le montant de la mise du joueur
                resp.setParcours(true);
                resp.setTexte("<strong>"+ask.getJ()+"</strong> décide de suivre");
                break;
            }
            case Const.CHECK : 
            {
                System.out.println("demande de check ");
                resp.setParcours(true);
                resp.setTexte("<strong>"+ask.getJ()+"</strong> a fait un check");
                break;
            }
            // le client a demandé a se coucher
            case Const.FOLD : 
            {
                System.out.println("demande de fold ");
                desk.getParticipantTour().remove(zone); // enleve le joueur de la liste des participants en cours
                // si je suis Dealer je passe le jeton a quelqu'un d'autre
                if(zone.equals(desk.getDealer())&&desk.getParticipantTour().size()>0)
                {
                    desk.setDealer(desk.getParticipantTour().get(0));
                }
                resp.setParcours(true);
                resp.setTexte("<strong>"+ask.getJ()+"</strong> se couche");
                break;
            }
            case Const.RAISE :
            {
                Mise ma_mise=desk.getMises(zone);
                float mnt=ask.getMontant();
                zone.setMontantPartie(zone.getMontantPartie()-mnt); // met a jour le montant que possède le joueur
                ma_mise.setMontantTotal(ma_mise.getMontantTotal()+mnt); // met a jour le montant de la mise du joueur
                resp.setParcours(true);
                resp.setTexte("<strong>"+ask.getJ()+"</strong> a surencherit");
                break;
            }
            case Const.QUIT:
            {
                System.out.println("Demande de quitt");
                //on supprime ce client de la table
                try {
                ListeClient.remove(ListeClient.indexOf(new Client(zone.getId())));
                ListeClient.remove(ListeObservateur.indexOf(new Client(zone.getId())));
                }catch(Exception ep) {}
                desk.getPlaces().remove(zone);
            }
            case Const.ALWAYS_CALL : {}
            case Const.ALWAYS_CHECK : {}
            case Const.ALWAYS_CHECK_FOLD : {}
            case Const.ALWAYS_FOLD : {}
            default:{}

        }
        //ouvreture des cartes (flop, tournant, riviere)
        boolean ouverte=false;
        if(zone!=null)
        if(zone.equals(desk.getDealer()))
        {
            desk.Controlleur=true;
        }
        if(Mise.isComplete(desk.getMises(),desk.getParticipantTour())&&desk.islaunch&&desk.Controlleur)
        {
            if(desk.getFlop()[0]==null) desk.SetFlop();
            else if(desk.getTournant()==null) desk.SetTournant();
                else if(desk.getRiviere()==null) desk.SetRiviere();
                else // on est a la fin d'un tour
                {
                    resp.setFinTour(true);
                }
            ouverte=true;
            desk.Controlleur=false;
        }

        // on désigne le joueur suivant comme étant celui qui effectura le prochain choix
        if(resp.isParcours())
        {
            if(desk.getParticipantTour().size()>0)
            {
                if(desk.getParticipantTour().size()==1)
                {
                    // le gagnant est forcément celui qui reste
                    float total=Mise.Total(desk.getMises());
                    desk.getParticipantTour().get(0).setMontantPartie(desk.getParticipantTour().get(0).getMontantPartie()+total);
                    resp.setFinTour(true);
                }
                if(ouverte)
                    desk.setEncoursDeJeu(desk.getParticipantTour().get((desk.getPlaces().indexOf(desk.getDealer())+1)%desk.getParticipantTour().size()));
                else
                    desk.setEncoursDeJeu(desk.getParticipantTour().get((desk.getPlaces().indexOf(zone)+1)%desk.getParticipantTour().size()));
            }
                //else resp.setParcours(false); // recommence un autre tour
        }
        //desk.setEncoursDeJeu(desk.getPlaces((zone.getId()+1)%desk.getChaises().size()));

        //
        System.out.println("Liste de places ="+desk.getPlaces());
        System.out.println("Liste de obsv ="+desk.getObservateurs());

        // chargement des elts de reponse
        resp.setFlop(desk.getFlop());
        resp.setTournant(desk.getTournant());
        resp.setRiviere(desk.getRiviere());
        resp.setActif(desk.getEncoursDeJeu());
        resp.setD(desk.getD());
        resp.setMises(desk.getMises());
        resp.setPlaces(desk.getPlaces());
        // envoi des reponse à tous les clients de la meme table
        
        for(int i=0;i<desk.getPlaces().size();i++)
        {
            Place play=desk.getPlaces().get(i);
            resp.setObs(false);
            //System.out.println("place--> "+play);
            if(play!=null)
            {
                ObjectOutputStream OUT=null; // flux d'écriture réseau
                try {
                    ObjectOutputStream cl=ListeClient.get(ListeClient.indexOf(new Client(play.getId()))).getSock_Client();
                    OUT=cl;
                    OUT.reset();
                    OUT.writeObject(resp);
                    OUT.flush();
                } catch(IOException e)
                {
                    //on supprime ce client de la table
                    ListeClient.remove(ListeClient.indexOf(new Client(play.getId())));
                    desk.getPlaces().remove(i);
                    //e.printStackTrace();
                }
            }
        }

        for(int i=0;i<desk.getObservateurs().size();i++)
        {
            Place play=desk.getObservateurs().get(i);
            resp.setObs(true);
            System.out.println("place observés--> "+play);
            if(play!=null)
            {
                ObjectOutputStream OUT=null; // flux d'écriture réseau
                try {
                    Client cust=new Client(play.getId());
                    System.out.println("Liste ob--> "+ListeObservateur);
                    System.out.println("cust--> "+cust);
                    ObjectOutputStream cl=ListeObservateur.get(ListeObservateur.indexOf(cust)).getSock_Client();
                    OUT=cl;
                    OUT.reset();
                    OUT.writeObject(resp);
                    OUT.flush();
                } catch(IOException e) {
                    System.out.println("except 1");
                    //on supprime ce client de la table
                    ListeObservateur.remove(ListeObservateur.indexOf(new Client(play.getId())));
                    desk.getObservateurs().remove(i);
                    //e.printStackTrace();
                }
                catch (ArrayIndexOutOfBoundsException e)
                {
                    System.out.println("except 2");
                    desk.getObservateurs().remove(i);
                    i--;
                }
            }
        }

        System.out.println("Liste client= "+getListeClient());
    }

    // Lancement du serveur
    public void Launch() {
        int port=0;
        // variable d'instance
        // y-a-t-il un argument
        port=1989; //erreur(syntaxe,1);
        // le port doit être entier >0
        boolean erreurPort=false;
        Exception E=null;
        erreurPort=port <=0;
        if(erreurPort) erreur("\n"+"Port incorrect ("+E+")",2);

        // on crée le servive d'écoute
        ServerSocket ecoute=null;
        int nbClients=0; // nbre de clients traités
        try{
            // on crée le service
            ecoute=new ServerSocket(port);
            // suivi
            System.out.println("Serveur générique lancé sur le port " + port);
            // boucle de service aux clients
            Socket client=null;
            while (true){ // boucle infinie - sera arrêtée par Ctrl-C
                client=ecoute.accept(); // attente d'un client
                nbClients++; // le service est assuré des threads séparés
                // on crée le thread de lecture
                new ServeurReceive(this,client,nbClients).start();
                // on retourne à l'écoute des demandes
            }
        }catch(Exception ex){
            erreur("L'erreur suivante s'est produite : " + ex.getMessage(),3);
            ex.printStackTrace();
        }
    }

    /**
     * Méthode qui affiche les erreurs qui sont générés pendant le lancement du client
     * @param msg message du l'erreur
     * @param exitCode code de sortie
     */
    public static void erreur(String msg, int exitCode){
        System.err.println(msg);
        System.exit(exitCode);
    }
}
/**
 * Classe permettant au serveur de recevoir une demande envoyée par un client sous forme de Thread
 * @author willy
 */
class ServeurReceive extends Thread{
    Socket client; // le client tcp
    int numClient; // n° de client
    serveurTCP serveur;

    public ServeurReceive(serveurTCP serv,Socket client, int numClient){
        this.client=client;
        this.numClient=numClient;

        serveur=serv;
    }

    public void run(){
        // données locales
        ObjectOutputStream OUT=null; // flux d'écriture réseau
        ObjectInputStream IN=null; // flux lecture réseau
        Demande reponse=null; // demande du client
        // suivi
        System.out.println("Thread de lecture des demandes du client "+ numClient + " lancé");

        try{
            if(!client.isInputShutdown())
            {
                OUT=new ObjectOutputStream(client.getOutputStream());
            
                // boucle lecture lignes de texte du flux IN
                while(client.isConnected()){
                    // création du flux lecture réseau
                    IN=new ObjectInputStream(client.getInputStream());
                    
                    // lecture flux réseau
                    try {
                        reponse=(Demande)IN.readObject();
                    }catch(EOFException eofex) {continue;}

                    if(reponse!=null) {
                    serveur.MAJ(OUT,reponse); // mise a jour des tables serveurs
                    }
                }
            }else
            {
                serveur.getListeClient().remove(Client.getClient(serveur.getListeClient(),OUT));
            }
        }
        catch (SocketException exs)
        {
            System.err.println("L'erreur suivante s'est produite : " + exs.getMessage());
            serveur.getListeClient().remove(Client.getClient(serveur.getListeClient(),OUT));
        }
        catch(Exception ex){
            System.err.println("L'erreur suivante s'est produite : " + ex.getMessage());
            ex.printStackTrace();
        }
        // fin - on ferme les flux
        try{
            OUT.close();IN.close();client.close();
        }catch(Exception ex){}
        // on signale la fin du thread
        System.out.println("[fin du Thread de lecture des demandes du client "+ numClient+"]");
    }//run
}//classe